/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2019年12月20日
 * V4.0
 */
package com.jphenix.driver.serialize;

//#region 【引用区】
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.jphenix.driver.json.Json;
import com.jphenix.driver.nodehandler.FNodeHandler;
import com.jphenix.kernel.baseobject.instanceb.ABase;
import com.jphenix.servlet.common.ActionContext;
import com.jphenix.servlet.multipart.instancea.UploadFile;
import com.jphenix.share.lang.SDate;
import com.jphenix.share.lang.SListMap;
import com.jphenix.share.tools.Base64;
import com.jphenix.share.tools.FileCopyTools;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.exceptions.MsgException;
import com.jphenix.standard.servlet.IActionContext;
import com.jphenix.standard.servlet.api.IRequest;
import com.jphenix.standard.servlet.api.IResponse;
import com.jphenix.standard.viewhandler.INodeHandler;
import com.jphenix.standard.viewhandler.IViewHandler;
//#endregion

//#region 【说明去】
/**
 * 将对象实例序列化成XML格式字符串
 * com.jphenix.driver.serialize.XmlSerializer
 * 
 * 2020-01-20 修改了处理ViewHandler对象错误
 *            增加了解析返回的异常对象，通过参数判断是否抛出该异常
 * 2020-03-18 修改了远程方法调用传递参数解析时报错
 * 2020-03-28 增加了MsgException异常序列与反序列化
 * 2020-04-27 增加了远程调用无返回值处理
 * 2020-07-02 修改了序列化时拼装XML的bug
 * 2020-07-11 去掉了替代null的对象，容易报错
 * 2021-03-16 整理了代码
 * 2022-02-08 不能采用Class.forName方式构建类，因为要构建的类所在的包可能不在 /WEB-INF/classes 中，可能在其子文件夹中
 * 2022-09-04 隔离了ServletApi，兼容新老Tomcat
 * 
 * @author MBG
 * 2019年12月20日
 */
//#endregion
@BeanInfo({"xmlserializer"})
@ClassInfo({"2024-07-14 11:42","将对象实例序列化成XML格式字符串"})
public class XmlSerializer extends ABase implements ISerializer {

	//#region serialize(obj) 将类实例序列化成XML格式字符串
	/**
	 * 将类实例序列化成XML格式字符串
	 * 注意：该方法返回的并不是完整的XML，只是参数被序列化成XML信息段
	 * @param obj 类实例
	 * @return    对应的XML格式字符串
	 * 2019年12月20日
	 * @author MBG
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public String serialize(Object obj) {
		if(obj==null) {
			return "<v t=\"NULL\"></v>";
		}
		StringBuffer res = new StringBuffer(); //构建返回值
		String typeName;                       //类型名
		if(obj.getClass().isArray()) {
			//该值是否为数组
			typeName = obj.getClass().getName();
			typeName = typeName.substring(2,typeName.length()-1); //去掉[Ljava.lang.String;开头的[L和末尾的;
			res.append("<v array=\"1\" t=\"").append(typeName).append("\">");
			int count = Array.getLength(obj); //数组数量
			for(int i=0;i<count;i++) {
				res.append(serialize(Array.get(obj,i)));
			}
			res.append("</v>");
			return res.toString();
		}

		/*
		 * 单实例
		 */
		if(obj instanceof IActionContext) {
			/*
			 * 动作类
			 */
			res.append("<v action=\"1\">");
			//获取提交参数容器
			Map<String,String[]> paraMap = ((IActionContext)obj).getRequest().getParameterMap();
			if(paraMap!=null) {
				//参数主键序列
				List<String> keyList = BaseUtil.getMapKeyList(paraMap);
				String[] values; //参数值
				for(String key:keyList) {
					res.append("<k n=\"").append(key).append("\">");
					values = paraMap.get(key);
					if(values!=null && values.length>0) {
						for(int i=0;i<values.length;i++) {
							res.append("<e>").append(values[i]).append("</e>");
						}
					}
					res.append("</k>");
				}
			}
			res.append("</v>");
			return res.toString();
		}
		typeName = obj.getClass().getName();
		if(obj instanceof Map) {
			/*
			 * -----Map-----
			 * Map容器
			 */
			res.append("<v map=\"1\" t=\"").append(typeName).append("\">");
			//获取容器主键序列
			List<String> keys = BaseUtil.getMapKeyList((Map)obj);
			for(String key:keys) {
				res
				.append("<k n=\"").append(key).append("\">")
				.append(serialize(((Map)obj).get(key)))
				.append("</k>");
			}
			res.append("</v>");
		}else if("com.jphenix.share.lang.SListMap".equals(typeName)) {
			/*
			 * -----SListMap-----
			 * 序列对照容器
			 */
			res.append("<v t=\"").append(typeName).append("\">");
			//获取容器主键序列
			List<String> keys = ((SListMap)obj).keys();
			for(String key:keys) {
				res
				.append("<k n=\"").append(key).append("\">")
				.append(serialize(((SListMap)obj).get(key)))
				.append("</k>");
			}
			res.append("</v>");
		}else if(obj instanceof List) {
			/*
			 * -----List-----
			 * List对象序列
			 */
			res.append("<v list=\"1\" t=\"").append(typeName).append("\">");
			for(Object ele:((List)obj)) {
				res.append(serialize(ele));
			}
			res.append("</v>");
		}else if("com.jphenix.standard.viewhandler.IViewHandler".equals(typeName)) {
			/*
			 * -----IViewHandler-----
			 * XML对象
			 */
			res
			.append("<v t=\"").append(typeName).append("\" action=\"1\">")
			.append(Base64.base64Encode(((IViewHandler)obj).getNodeBody(),"UTF-8"))
			.append("</v>");
		}else if(obj instanceof MsgException) {
			/*
			 * -----MsgException-----
			 * 消息异常
			 */
			res
			.append("<v msg_exception=\"1\" head=\">").append(((MsgException)obj).getHead())
			.append("\" code=\"").append(((MsgException)obj).getCode()).append("\">")
			.append(Base64.base64Encode(((MsgException) obj).getMessage(),"UTF-8")).append("</v>");
		}else if(obj instanceof Exception) {
			/*
			 * -----Exception-----
			 * 异常
			 */
			res
			.append("<v exception=\"1\" t=\"").append(typeName).append("\">")
			.append(Base64.base64Encode(((Exception) obj).getMessage(),"UTF-8")).append("</v>");
		}else if("java.io.File".equals(typeName)) {
			/*
			 * -----File-----
			 * 文件对象
			 */
			try {
				res
				.append("<v t=\"").append(typeName).append("\" n=\"")
				.append(((File)obj).getName()).append("\" l=\"")
				.append(((File)obj).length()).append("\"><![CDATA[")
				.append(Base64.base64Encode(FileCopyTools.copyToByteArray((File)obj)))
				.append("]]></v>"); 
			}catch(Exception e) {
				e.printStackTrace();
			}
		}else if("com.jphenix.servlet.multipart.instancea.UploadFile".equals(typeName)) {
			/*
			 * -----UploadFile-----
			 * 文件上传对象
			 */
			try {
				File pFile = ((UploadFile)obj).getFile();
				res.append("<v t=\"").append(typeName).append("\" n=\"")
				.append(((UploadFile)obj).srcFileName).append("\" l=\"")
				.append(pFile.length()).append("\"><![CDATA[")
				.append(Base64.base64Encode(FileCopyTools.copyToByteArray(pFile)))
				.append("]]></v>"); 
			}catch(Exception e) {
				e.printStackTrace();
			}
		}else if(obj instanceof ISerializable) {
			/*
			 * -----ISerializable-----
			 */
			res.append("<v iserializable=\"1\" t=\"").append(typeName).append("\">").append(Base64.base64Encode(((ISerializable)obj).serialize(),"UTF-8")).append("</v>");
		}else {
			/*
			 * String int Integer long Long doble Double float Float Json boolean Boolean
			 */
			res.append("<v t=\"").append(typeName).append("\">").append(Base64.base64Encode(obj.toString(),"UTF-8")).append("</v>");
		}
		return res.toString();
	}
	//#endregion

	//#region unserialize(xmlStr,bf,req,resp,triggerException) 将XML格式字符串反序列化为对应的类实例
	/**
	 * 将XML格式字符串反序列化为对应的类实例
	 * @param xmlStr           XML格式字符串
	 * @param req              请求对象（可为空，只有处理IActionContext实例时才会用到）
	 * @param resp             反馈对象（可为空，只有处理IActionContext实例时才会用到）
	 * @param triggerException 如果解析的值未异常对象，是否触发抛出该异常
	 * @return                 对应的类实例
	 * @throws Exception       异常
	 */
	public Object unserialize(String xmlStr,IRequest req,IResponse resp,boolean triggerException) throws Exception {
		//构建XML对象
		INodeHandler infoXml = FNodeHandler.newNodeHandler();
		infoXml.setNodeBody(xmlStr);
		return unserialize(infoXml,req,resp,triggerException);
	}
	//#endregion

	//#region unserialize(xmlStr,triggerException) 将XML格式字符串反序列化为对应的类实例
	/**
	 * 将XML格式字符串反序列化为对应的类实例
	 * @param xmlStr           XML格式字符串
	 * @param triggerException 如果解析的值未异常对象，是否触发抛出该异常
	 * @return                 对应的类实例
	 * @throws Exception       异常
	 * 2019年12月23日
	 * @author MBG
	 */
	public Object unserialize(String xmlStr,boolean triggerException) throws Exception {
		//构建XML对象
		INodeHandler infoXml = FNodeHandler.newNodeHandler();
		infoXml.setNodeBody(xmlStr);
		return unserialize(infoXml,null,null,triggerException);
	}
	//#endregion

	//#region unserialize(node,bf,req,resp,triggerException) 将XML对象反序列化为对应的类实例
	/**
	 * 将XML对象反序列化为对应的类实例
	 * @param node             XML对象
	 * @param bf               主过滤器
	 * @param req              请求对象（可为空，只有处理IActionContext实例时才会用到）
	 * @param resp             反馈对象（可为空，只有处理IActionContext实例时才会用到）
	 * @param triggerException 如果解析的值未异常对象，是否触发抛出该异常
	 * @return                 对应的类实例
	 * 2019年12月20日
	 * @author MBG
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Object unserialize(
			IViewHandler node,
			IRequest     req,
			IResponse    resp,
			boolean      triggerException) throws Exception {
		if(node==null) {
			return null;
		}
		if(!node.nn().equals("v")) {
			node = node.getFirstChildNodeByNodeName("v");
		}
		String type = node.a("t"); //对象类型
		if("NULL".equals(type)) {
			//返回空
			return null;
		}
		Object res       = null; //返回值
		String typeName  = null; //类型名
		if("1".equals(node.a("array"))) {
			//数组对象
			List<IViewHandler> clist = node.ocnn("v"); //返回一级子节点序列
			typeName                 = node.a("t");    //类型名
			//构造数组
			res = Array.newInstance(getBeanFactory().getClassLoader().loadClass(typeName),clist.size());

			// 不能采用Class.forName方式构建类，因为要构建的类所在的包可能不在 /WEB-INF/classes 中，可能在其子文件夹中
			//res = Array.newInstance(Class.forName(typeName),clist.size());
			for(int i=0;i<clist.size();i++) {
				Array.set(res,i,unserialize(clist.get(i),req,resp,triggerException));
			}
			return res;
		}
		if("1".equals(node.a("action"))) {
			/*
			 * ----------action-----------
			 */
			//动作上下文
			ActionContext ac = new ActionContext(req,resp,req.getServerName().toLowerCase(),"","");
			//提交参数容器
			Map<String,String[]> paraMap = req.getParameterMap();
			//获取提交参数容器
			List<IViewHandler> eleNodes = node.ocnn("k");
			String key; //参数主键
			List<String> valueList;
			List<IViewHandler> valueNodes;
			for(IViewHandler ele:eleNodes) {
				key = ele.a("name");
				valueList = new ArrayList<String>();
				valueNodes = ele.ocnn("e");
				for(IViewHandler cele:valueNodes) {
					valueList.add(cele.nt());
				}
				paraMap.put(key,StringUtil.list2strs(valueList));
			}
			return ac;
		}
		//消息异常 该对象不需要反射构建
		if("1".equals(node.a("msg_exception"))) {
			//构建异常
			MsgException me = new MsgException(node.a("head"),Base64.base64Decode(node.nt(),"UTF-8"),node.a("code"));
			if(triggerException) {
				throw me;
			}
			return me;
		}
		/*
		 * 单元素对象
		 */
		typeName    = node.a("t");
		if(typeName.length()<1) {
			return null;
		}
		//返回值类型
		Class reCls = null;
		try {
			reCls = getBeanFactory().getClassLoader().loadClass(typeName);
			//不能采用Class.forName方式构建类，因为要构建的类所在的包可能不在 /WEB-INF/classes 中，可能在其子文件夹中
			//reCls = Class.forName(typeName);
		}catch(Exception e) {
			if(log==null) {
				System.err.println("loadClass:("+typeName+") Exception happend; XML:[\n"+node.getNodeBody()+"\n]");
			}else {
				error("loadClass:("+typeName+") Exception happend; XML:[\n"+node.getNodeBody()+"\n]");
			}
			throw e;
		}
		//异常
		if("1".equals(node.a("exception"))) {
			//构建异常
			Constructor constructor = reCls.getConstructor(new Class[] {String.class});
			res = constructor.newInstance(new Object[] {Base64.base64Decode(node.nt(),"UTF-8")});
			if(triggerException) {
				throw (Exception)res;
			}
			return res;
		}
		if("1".equals(node.a("iserializable"))) {
			/*
			 * ----------ISerializable-----------
			 */
			//自行序列化的类
			res = reCls.getDeclaredConstructor((Class[])null).newInstance((Object[])null);
			((ISerializable)res).unserialize(Base64.base64Decode(node.nt(),"UTF-8"));
		}else if("1".equals(node.a("map"))) {
			/*
			 * ----------Map-----------
			 */
			res = reCls.getDeclaredConstructor((Class[])null).newInstance((Object[])null); //对照容器
			List<IViewHandler> keys = node.ocnn("k");      //元素信息序列
			for(IViewHandler ele:keys) {
				((Map)res).put(ele.a("n"),unserialize(ele.fc(),req,resp,triggerException));
			}
		}else if("com.jphenix.share.lang.SListMap".equals(typeName)) {
			/*
			 * ----------SListMap-----------
			 */
			res = new SListMap();      //对照容器
			List<IViewHandler> keys = node.ocnn("k");      //元素信息序列
			for(IViewHandler ele:keys) {
				((SListMap)res).put(ele.a("n"),unserialize(ele.fc(),req,resp,triggerException));
			}
		}else if("1".equals(node.a("list"))) {
			/*
			 * ----------List-----------
			 */
			res = reCls.getDeclaredConstructor((Class[])null).newInstance((Object[])null); //序列对象
			List<IViewHandler> keys = node.ocnn("v");      //元素信息序列
			for(IViewHandler ele:keys) {
				((List)res).add(unserialize(ele,req,resp,triggerException));
			}
		}else if("com.jphenix.standard.viewhandler.IViewHandler".equals(typeName) 
				|| "com.jphenix.driver.nodehandler.instancea.NodeHandler".equals(typeName)) {
			/*
			 * ----------IViewHandler-----------
			 */
			res = FNodeHandler.newBody(Base64.base64Decode(node.nt(),"UTF-8"));
		}else if("java.io.File".equals(typeName)) {
			/*
			 * ----------File-----------
			 */
			res = tmpFile("/"+SDate.nowThinDate()+"/"+node.a("name"));
			//解析文件数据
			FileCopyTools.copy(Base64.decode64(node.nt()),(File)res);
		}else if("com.jphenix.servlet.multipart.instancea.UploadFile".equals(typeName)) {
			/*
			 * ----------UploadFile-----------
			 */
			File file = tmpFile("/"+SDate.nowThinDate()+"/"+node.a("name"));
			//解析文件数据
			FileCopyTools.copy(Base64.decode64(node.nt()),file);
			res = UploadFile.file(file,node.a("name"));
		}else if("com.jphenix.driver.json.Json".equals(typeName)) {
			/*
			 * ----------Json-----------
			 */
			res = new Json(Base64.base64Decode(node.nt(),"UTF-8"));
		}else if("java.lang.String".equals(typeName)) {
			/*
			 * ----------String-----------
			 */
			res = Base64.base64Decode(node.nt(),"UTF-8");
		}else if("int".equals(typeName) || "java.lang.Integer".equals(typeName)) {
			/*
			 * ----------int Integer-----------
			 */
			res = sint(Base64.base64Decode(node.nt(),"UTF-8"));
		}else if("long".equals(typeName) || "java.lang.Long".equals(typeName)) {
			/*
			 * ----------long Long-----------
			 */
			res = lon(Base64.base64Decode(node.nt(),"UTF-8"));
		}else if("float".equals(typeName) 
				|| "double".equals(typeName) 
				|| "java.lang.Float".equals(typeName)
				|| "java.lang.Double".equals(typeName)) {
			/*
			 * ----------float Float double Double-----------
			 */
			res = dou(Base64.base64Decode(node.nt(),"UTF-8"));
		}else if("long".equals(typeName) || "java.lang.Long".equals(typeName)) {
			/*
			 * ----------boolean Boolean-----------
			 */
			res = boo(Base64.base64Decode(node.nt(),"UTF-8"));
		}else if("byte".equals(typeName) || "java.lang.Byte".equals(typeName)) {
			/*
			 * ----------byte Byte-----------
			 */
			byte[] bytes = str(Base64.base64Decode(node.nt(),"UTF-8")).getBytes();
			if(bytes!=null && bytes.length>0) {
				res = bytes[0];
			}else {
				res = null;
			}
		}else {
			res = Base64.base64Decode(node.nt(),"UTF-8");
		}
		return res;
	}
	//#endregion
}









